﻿namespace Ngs
{
    using System;

    /// <summary>
    /// Represents a data range based on a generic type.
    /// </summary>
    /// <typeparam name="TRangeType"></typeparam>
    public class Range<T> : IRange<T>, IEquatable<Range<T>>
        where T : IComparable<T>
    {

        public Range()
        {
        }

        public Range(T min, T max)
        {
            this.Min = min;
            this.Max = max;
        }

        /// <summary>
        /// Gets or sets the minimum value of the range.
        /// </summary>
        public T Min { get; set; }

        /// <summary>
        /// Gets or sets the maximum value of the range.
        /// </summary>
        public T Max { get; set; }

        protected void VerifyState()
        {
            ExceptionFactory.ThrowInvalidOperation(Min == null, "MinRange is not set.");
            ExceptionFactory.ThrowInvalidOperation(Max == null, "MaxRange is not set.");
        }

        /// <summary>
        /// Verifies that the specified range value is an appropriate value for the range.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        protected virtual bool VerifyRangeValue(T value)
        {
            VerifyState();
            return (Min.CompareTo(value) >= 0 && Max.CompareTo(value) <= 0);
        }

        public bool Equals(Range<T> other)
        {
            if (other == null)
            {
                return false;
            }

            Func<Range<T>, bool> predicateBuilder = null;

            if (this.Min != null && other.Min != null)
            {
                predicateBuilder += o => this.Min.CompareTo(o.Min) == 0;
            }

            if (this.Max != null && other.Max != null)
            {
                predicateBuilder += o => this.Max.CompareTo(o.Max) == 0;
            }

            if (predicateBuilder == null)
            {
                // Since all variables are null and thus equal.
                return true;
            }

            return predicateBuilder(other);
        }

        public override bool Equals(object obj)
        {
            Range<T> range = obj as Range<T>;
            return this.Equals(range);
        }

        public override int GetHashCode()
        {
            int num = (this.Min == null) ? 1 : (this.Min.GetHashCode() + 1);
            int num2 = (this.Max == null) ? 1 : (this.Max.GetHashCode() + 1);
            return (num * num2);
        }


        #region IRange Members

        object IRange.MinValue
        {
            get { return this.Min; }
            set { this.Min = (T)value; }
        }

        object IRange.MaxValue
        {
            get { return this.Max; }
            set { this.Max = (T)value; }
        }

        #endregion
    }
}
